Skip to content

Add SCRAM-SHA-256-PLUS support#776

Merged
josevalim merged 5 commits into
elixir-ecto:masterfrom
Snehil-Shah:channel-binding
Jul 5, 2026
Merged

Add SCRAM-SHA-256-PLUS support#776
josevalim merged 5 commits into
elixir-ecto:masterfrom
Snehil-Shah:channel-binding

Conversation

@Snehil-Shah

Copy link
Copy Markdown
Contributor

This adds channel binding support for SCRAM auth handshake, enabling better security with servers that support SCRAM-SHA-256-PLUS.

Also adds a channel_binding knob with three modes: :prefer, :require, and :disable. (all self-explanatory)

I tested the flow locally on PG 16 with SSL enabled and SCRAM auth (so server advertises SCRAM-SHA-256-PLUS) in all three of the above modes, works as expected.

Proposal: https://groups.google.com/g/elixir-ecto/c/Flxp0Krz6hY

Signed-off-by: Snehil Shah <snehilshah.989@gmail.com>
Signed-off-by: Snehil Shah <snehilshah.989@gmail.com>
Comment thread lib/postgrex/scram.ex
Comment thread lib/postgrex.ex
Comment thread test/login_test.exs

assert error_log =~ "expected :channel_binding to be :prefer, :require or :disable"
end
end

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How easy it is to add a test for channel_binding: :require over non-SSL?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Easy and obvious, I'll add it right away!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@josevalim Added that test and also added a test for :prefer. Although, the :disable and :prefer tests don't really test the actual negotiation logic. It's just a sanity test. Can be removed, or we can work on finding a way to test the actual negotiation logic.. WDYT?

Signed-off-by: Snehil Shah <snehilshah.989@gmail.com>
Signed-off-by: Snehil Shah <snehilshah.989@gmail.com>
@Snehil-Shah Snehil-Shah requested a review from josevalim July 3, 2026 20:35
Comment thread lib/postgrex/scram.ex Outdated

# Fallback
:error ->
{:ok, {"SCRAM-SHA-256", "y,,", ""}}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we send n,, when the mode is :prefer? Because I believe sending y,, will cause Postgres to reject the authentication

@Snehil-Shah Snehil-Shah Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@greg-rychlewski Thank you for catching this. n,, is the correct header here, I have pushed the change.

Thinking out loud, does it also hurt sending n,, instead of y,, when the server didn't offer -PLUS? Is there some backward compatibility benefit to this, given we used to always send n,, before? y,, is more semantically correct and lets the server know what's up, and I tested it on PG 10 that supports SCRAM but no channel binding, and it accepts y,, with no issues. Any case where this might not be backward-compatible?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think y,, is correct because it protects from downgrade attacks. In terms of backwards compatibility I'm not super familiar with this protocol if anything supporting SCRAM is supposed to be accepting y,,. If not this could be an issue for other databases that implement the same wire protocol like Yugabyte and Cockroach.

If this is an issue I can think of a few things:

  1. The default shouldn't be prefer it should be disable. But this is a pretty weak default.
  2. We make prefer weaker so that it doesn't protect against downgrade attacks. This seems bad
  3. We default prefer and let some things fail and then tell them to switch the mode if their database doesn't work with it

So all these seem kind of bad I'm hoping everything just needs to parse y,, out of the box. But we'd have to check. It would be good to see what @josevalim thinks too

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are probably overthinking this. y,, is part of the standard SCRAM grammar, so should be a safe (& obvious) assumption that servers would do it right. For confidence, libpq has the same semantics in their SCRAM logic, and I think it can be a good standard to follow.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am good with that! @josevalim im approving but wont merge just to be sure you saw this convo and are ok with the conclusion as well

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Snehil-Shah thanks for the contribution btw. This is a nice feature

Signed-off-by: Snehil Shah <snehilshah.989@gmail.com>
@josevalim josevalim merged commit b9c954d into elixir-ecto:master Jul 5, 2026
13 checks passed
@josevalim

Copy link
Copy Markdown
Member

💚 💙 💜 💛 ❤️

@Snehil-Shah Snehil-Shah deleted the channel-binding branch July 5, 2026 11:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants